﻿using Devil;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Jobs;
using UnityEngine.Profiling;

namespace GameToolkit
{
    [System.Flags]
    public enum ETransformSensorFlag
    {
        Position = 1,
        Rotation = 2,
        Scale = 4,
    }

    public interface ITransformSensor
    {
        void OnTransformChanged(Transform trans, ETransformSensorFlag flags);
    }

    public interface IRelativeTransform
    {
        Vector3 localPosition { get; set; }
        Quaternion localRotation { get; set; }
        Transform relativeTransform { get; set; }
        void Set(Vector3 localPosition, Quaternion localRotation);
        void ClearBindData(bool clearPos, bool clearRot, bool clearKeepPlane);
        void KeepOnPlane(Vector3 pointInPlane, Vector3 normal);
        bool IsEnabled { get; set; }
        void Retain();
        void Release();
    }

    [ExecuteInEditMode, DisallowMultipleComponent, AddComponentMenu("")]
    public sealed class TransformSensor : MonoBehaviour, IBeforeCullingUpdate, IAfterCullingUpdate
    {
        #region update jobs
        // 跟踪 transform 数据
        internal struct SensorData
        {
            public Vector3 position;
            public Quaternion rotation;
            public Vector3 scale;
            public byte dirtyFlags;
        }

        // 回调 ITransformSensor ID
        internal struct InvokeID
        {
            public ushort index;
            public byte dirtyFlags;
            public InvokeID(ushort index, byte flags)
            {
                this.index = index;
                this.dirtyFlags = flags;
            }
        }

        internal enum EBindFlag : byte
        {
            Active = 0x1,
            // 忽略移动
            BindPosition = 0x2,
            // 忽略旋转
            BindRotation = 0x4,
            // 保持在平面上移动
            KeepInPlane = 0x8,

            SensorActive = 0x10,
            RelativeActive = 0x20,
        }

        // transform 绑定
        internal struct BindData
        {
            public EBindFlag flags; // EBindFlag

            public int id;
            public int bindId;

            public ushort sensorIndex; // index + 1, 自己所在 Sensor Index
            public ushort bindIndex; // index + 1, 绑定对象所在 Sensor Index

            public Vector3 bindPos;
            public Quaternion bindRot;

            public Vector3 planeP;
            public Vector3 planeN;

        }

        static readonly EBindFlag BindFlags = EBindFlag.BindPosition | EBindFlag.BindRotation;
        static readonly EBindFlag FindIndexFlags = EBindFlag.RelativeActive | EBindFlag.SensorActive;

#if BURST_COMPILE
        [BurstCompile]
#endif
        internal struct TrackTransformJob : IJobParallelForTransform
        {
            //public float trackPrecision;

            [NativeDisableParallelForRestriction]
            public NativeArray<SensorData> datas;

            public void Execute(int index, TransformAccess transform)
            {
                var data = datas[index];
                var pos = transform.position;
                var rot = transform.rotation;
                var scale = transform.localScale;
                if (pos != data.position)
                {
                    data.dirtyFlags |= (byte)ETransformSensorFlag.Position;
                    data.position = pos;
                }
                if (rot != data.rotation)
                {
                    data.dirtyFlags |= (byte)ETransformSensorFlag.Rotation;
                    data.rotation = rot;
                }
                if (scale != data.scale)
                {
                    data.dirtyFlags |= (byte)ETransformSensorFlag.Scale;
                    data.scale = scale;
                }
                datas[index] = data;
            }
        }

#if BURST_COMPILE
        [BurstCompile]
#endif
        internal struct FindBindIndexJob : IJobParallelFor
        {
            public int sensorSize;

            [NativeDisableParallelForRestriction]
            public NativeArray<BindData> binds;

            [ReadOnly, NativeDisableParallelForRestriction]
            public NativeArray<int> sensors;

            public void Execute(int index)
            {
                var bind = binds[index];
                bind.bindIndex = 0;
                bind.sensorIndex = 0;
                var flags = bind.flags & FindIndexFlags;
                EBindFlag findflag = 0;
                for (int i = 0; findflag != flags && i < sensorSize; i++)
                {
                    var sensor = sensors[i];
                    ushort ptr = (ushort)(i + 1);
                    if (sensor == bind.id)
                    {
                        bind.sensorIndex = ptr;
                        findflag |= EBindFlag.SensorActive;
                    }
                    if (sensor == bind.bindId)
                    {
                        bind.bindIndex = ptr;
                        findflag |= EBindFlag.RelativeActive;
                    }
                }
                binds[index] = bind;
            }
        }

#if BURST_COMPILE
        [BurstCompile]
#endif
        internal struct BindTransformJob : IJobParallelForTransform
        {
            [NativeDisableParallelForRestriction]
            public NativeArray<BindData> binds;

            [NativeDisableParallelForRestriction]
            public NativeArray<SensorData> sensors;

            public void Execute(int index, TransformAccess transform)
            {
                var bind = binds[index];
                if ((bind.flags & EBindFlag.Active) == 0)
                    return;
                Vector3 pos;
                Quaternion rot;
                if ((bind.flags & EBindFlag.RelativeActive) == 0)
                {
                    if ((bind.flags & EBindFlag.BindPosition) != 0)
                    {
                        pos = bind.bindPos;
                        if ((bind.flags & EBindFlag.KeepInPlane) != 0)
                        {
                            pos = Vector3.ProjectOnPlane(pos - bind.planeP, bind.planeN) + bind.planeP;
                        }
                        transform.position = pos;
                    }
                    else
                    {
                        pos = transform.position;
                    }
                    if ((bind.flags & EBindFlag.BindRotation) != 0)
                    {
                        rot = bind.bindRot;
                        transform.rotation = rot;
                    }
                    else
                    {
                        rot = transform.rotation;
                    }
                }
                else if (bind.bindIndex > 0)
                {
                    var sensor = sensors[bind.bindIndex - 1];
                    if ((bind.flags & EBindFlag.BindPosition) != 0)
                    {
                        pos = sensor.position + (sensor.rotation * bind.bindPos);
                        if ((bind.flags & EBindFlag.KeepInPlane) != 0)
                        {
                            pos = Vector3.ProjectOnPlane(pos - bind.planeP, bind.planeN) + bind.planeP;
                        }
                        transform.position = pos;
                    }
                    else
                    {
                        pos = transform.position;
                    }
                    if ((bind.flags & EBindFlag.BindRotation) != 0)
                    {
                        rot = sensor.rotation * bind.bindRot;
                        transform.rotation = rot;
                    }
                    else
                    {
                        rot = transform.rotation;
                    }
                }
                else
                {
                    pos = transform.position;
                    rot = transform.rotation;
                }

                // 如果发生坐标改变，更新信息
                if ((bind.flags & EBindFlag.SensorActive) != 0 && bind.sensorIndex > 0)
                {
                    var sensor = sensors[bind.sensorIndex - 1];
                    if (sensor.position != pos)
                    {
                        sensor.position = pos;
                        sensor.dirtyFlags |= (byte)ETransformSensorFlag.Position;
                    }
                    if (sensor.rotation != rot)
                    {
                        sensor.rotation = rot;
                        sensor.dirtyFlags |= (byte)ETransformSensorFlag.Rotation;
                    }
                    sensors[bind.sensorIndex - 1] = sensor;
                }
            }
        }


#if BURST_COMPILE
        [BurstCompile]
#endif
        internal struct UpdateInvokeIDJob : IJob
        {
            public int size;

            [NativeDisableParallelForRestriction]
            public NativeArray<SensorData> datas;

            [NativeDisableParallelForRestriction]
            public NativeArray<InvokeID> ids;

            public void Execute()
            {
                int ptr = 0;
                for (int i = 0; i < size; i++)
                {
                    var data = datas[i];
                    if (data.dirtyFlags != 0)
                    {
                        ids[ptr++] = new InvokeID((ushort)i, data.dirtyFlags);
                        data.dirtyFlags = 0;
                        datas[i] = data;
                    }
                }
                if (ptr < size)
                {
                    ids[ptr] = new InvokeID(0, 0);
                }
            }
        }

        internal abstract class NativeTrans<T> : INative<T> where T : struct
        {
            int _id;
            Transform mTrans;
            public int InstanceID { get { return _id; } }
            public Transform transform { get { return mTrans; } }

            public NativeTrans(Transform trans)
            {
                _id = trans.GetInstanceID();
                mTrans = trans;
            }

            public abstract T GetNativeData();

            public override int GetHashCode()
            {
                return _id;
            }

            public override bool Equals(object obj)
            {
                var sen = obj as NativeTrans<T>;
                return sen != null && sen._id == this._id;
            }

        }

        internal class SensorNode : NativeTrans<SensorData>
        {
            internal LinkedList<ITransformSensor> sensors;
            internal NativeInstance<SensorData> nativeInstance;

            public SensorNode(Transform trans) : base(trans)
            {
                sensors = new LinkedList<ITransformSensor>();
            }

            public override SensorData GetNativeData()
            {
                SensorData dt;
                dt.position = transform.position;
                dt.rotation = transform.rotation;
                dt.scale = transform.localScale;
                dt.dirtyFlags = 0;
                return dt;
            }
        }

        public class Sensor
        {
            SensorNode sensor;
            LinkedListNode<ITransformSensor> node;
            public bool IsVaild { get { return sensor != null && node != null; } }
            internal Sensor(SensorNode sensor, LinkedListNode<ITransformSensor> node)
            {
                this.sensor = sensor;
                this.node = node;
            }

            public void Release()
            {
                if (sensor != null && node != null)
                {
                    sensor.sensors.Remove(node);
                    if (sensor.sensors.Count == 0)
                    {
                        sensor.nativeInstance.FlushNativeJobs();
                        sensor.nativeInstance.Release();
                    }
                    sensor = null;
                    node = null;
                }
            }
        }

        internal class BindNode : NativeTrans<BindData>, ITransformSensor, IRelativeTransform
        {
            int _bindId;
            Transform _relativeTo;
            internal Vector3 _pos;
            internal Quaternion _rot;
            internal Vector3 _planeP;
            internal Vector3 _planeN;
            NativePool _pool;
            NativeInstance<BindData> _nativeInstance;
            Sensor _targetSensor;
            uint _refers;
            EBindFlag _flags;

            internal BindNode(Transform trans) : base(trans)
            {
                _pos = trans.position;
                _rot = trans.rotation;
            }

            bool IsActive()
            {
                return (_flags & EBindFlag.Active) != 0 && (_flags & BindFlags) != 0;
            }

            public Transform relativeTransform
            {
                get { return _relativeTo; }
                set
                {
                    if (_relativeTo != value)
                    {
                        _relativeTo = value;
                        _bindId = value == null ? 0 : value.GetInstanceID();
                        if (_targetSensor != null)
                        {
                            _targetSensor.Release();
                            _targetSensor = null;
                        }
                        if (_pool != null && IsActive() && value != null)
                        {
                            _pool.FlushNativeJobs();
                            _targetSensor = _pool.CreateSensor(value, this);
                        }
                    }
                }
            }

            internal void Setup(NativePool pool, NativeInstance<BindData> nativeInstance)
            {
                if (pool != null && nativeInstance != null && nativeInstance.IsAllocated)
                {
                    _pool = pool;
                    _nativeInstance = nativeInstance;
                    //if(_relativeTo != null && _refers > 0)
                    //{
                    //    _targetSensor = _pool.CreateSensor(_relativeTo, this);
                    //}
                }
            }

            public Vector3 localPosition
            {
                get { return _pos; }
                set
                {
                    if (_pool != null)
                    {
                        _pos = value;
                        _flags |= EBindFlag.BindPosition;
                        if (IsActive())
                        {
                            _pool.FlushNativeJobs();
                            _nativeInstance.UpdateNativeData();
                        }
                    }
                }
            }

            public Quaternion localRotation
            {
                get { return _rot; }
                set
                {
                    if (_pool != null)
                    {
                        _rot = value;
                        _flags |= EBindFlag.BindRotation;
                        if (IsActive())
                        {
                            _pool.FlushNativeJobs();
                            _nativeInstance.UpdateNativeData();
                        }
                    }
                }
            }

            public void ClearBindData(bool clearPos, bool clearRot, bool clearPlane)
            {
                if (_pool != null)
                {
                    EBindFlag f = 0;
                    if (clearPos)
                        f |= EBindFlag.BindPosition;
                    if (clearRot)
                        f |= EBindFlag.BindRotation;
                    if (clearPlane)
                        f |= EBindFlag.KeepInPlane;
                    _flags &= ~f;
                    if (IsActive())
                    {
                        _pool.FlushNativeJobs();
                        _nativeInstance.UpdateNativeData();
                    }
                }
            }

            public void Set(Vector3 localPos, Quaternion localRot)
            {
                if (_pool != null)
                {
                    _flags |= BindFlags;
                    _pos = localPos;
                    _rot = localRot;
                    if (IsActive())
                    {
                        _pool.FlushNativeJobs();
                        _nativeInstance.UpdateNativeData();
                    }
                }
            }

            public void KeepOnPlane(Vector3 point, Vector3 normal)
            {
                if(_pool != null)
                {
                    _flags |= EBindFlag.KeepInPlane;
                    _planeP = point;
                    _planeN = normal.normalized;
                    if (IsActive())
                    {
                        _pool.FlushNativeJobs();
                        _nativeInstance.UpdateNativeData();
                    }
                }
            }

            void Activate(bool isActive)
            {
                _pool.FlushNativeJobs();
                _nativeInstance.UpdateNativeData();
                if (isActive && _relativeTo != null && _targetSensor == null)
                {
                    _targetSensor = _pool.CreateSensor(_relativeTo, this);
                }
                else if (_targetSensor != null)
                {
                    _targetSensor.Release();
                    _targetSensor = null;
                }
            }

            public bool IsEnabled
            {
                get { return (_flags & EBindFlag.Active) != 0 && _pool != null; }
                set
                {
                    if((_flags & EBindFlag.Active) != EBindFlag.Active && _pool != null)
                    {
                        var act = IsActive();
                        if (value)
                            _flags |= EBindFlag.Active;
                        else
                            _flags &= ~EBindFlag.Active;
                        if (act != IsActive())
                        {
                            Activate(!act);
                        }
                    }
                }
            }

            public override BindData GetNativeData()
            {
                EBindFlag flag = _flags;
                //if (_enable)
                //    flag |= EBindFlag.Active;
                if (transform != null)
                    flag |= EBindFlag.SensorActive;
                if (_relativeTo != null)
                    flag |= EBindFlag.RelativeActive;
                BindData dt = _nativeInstance == null ? default : _nativeInstance.AccessNativeData();
                dt.id = InstanceID;
                dt.flags = flag;
                dt.bindId = _bindId;
                dt.bindPos = _pos;
                dt.bindRot = _rot;
                dt.planeP = _planeP;
                dt.planeN = _planeN;
                return dt;
            }

            public void Retain()
            {
                _refers++;
            }

            public void Release()
            {
                if (_refers == 0)
                    throw new System.Exception("RelativeTransform was already released.");
                _refers--;
                if (_refers == 0)
                {
                    _flags &= ~EBindFlag.Active;
                    if (_pool != null && _nativeInstance != null)
                    {
                        _pool.FlushNativeJobs();
                        _nativeInstance.Release();
                        _pool.mFindBindIndex = true;
                    }
                    if(_targetSensor != null)
                    {
                        _targetSensor.Release();
                        _targetSensor = null;
                    }
                    _pool = null;
                    _nativeInstance = null;
                }
            }

            public void OnTransformChanged(Transform trans, ETransformSensorFlag flags)
            {
            }
        }

        internal class NativePool : NativeInstance<SensorData>.Pool
        {
            NativeArray<int> mSensorIDs;
            NativeArray<InvokeID> mInvokeIDs;
            NativeInstance<BindData>.Pool mBindPool;
            internal bool mFindBindIndex;

            public NativePool(int cap) : base(cap)
            {
                mInvokeIDs = new NativeArray<InvokeID>(cap, Allocator.Persistent);
                mSensorIDs = new NativeArray<int>(cap, Allocator.Persistent);
                mBindPool = new NativeInstance<BindData>.Pool(Mathf.Max(128, cap >> 1));
#if UNITY_EDITOR
                Debug.LogFormat("Alloc {0}KB native memery for TransformSensor", CalculateNativeSize(ENativeSize.KB));
#endif
            }

            public int IndexOfBinding(int id)
            {
                for (int i = 0; i < mBindPool.Length; i++)
                {
                    if (mBindPool[i].Instance.InstanceID == id)
                        return i;
                }
                return -1;
            }

            public Sensor CreateSensor(Transform trans, ITransformSensor sensor)
            {
                var id = trans.GetInstanceID();
                var index = IndexOf(id);
                SensorNode s;
                if (index == -1)
                {
                    if (Length >= ushort.MaxValue)
                        throw new System.Exception("size of TransformSensor was out of bounds(65535)");
                    s = new SensorNode(trans);
                    FlushNativeJobs();
                    s.nativeInstance = AddInstance(s, trans);
                    mFindBindIndex |= mBindPool.Length > 0;
                }
                else
                {
                    s = (SensorNode)this[index].Instance;
                }
                var node = s.sensors.AddLast(sensor);
                return new Sensor(s, node);
            }

            public IRelativeTransform GetRelativeTransform(Transform trans)
            {
                if (trans == null)
                    return default;
                var id = trans.GetInstanceID();
                var index = IndexOfBinding(id);
                BindNode bind;
                if (index == -1)
                {
                    FlushNativeJobs();
                    bind = new BindNode(trans);
                    var native = mBindPool.AddInstance(bind, trans);
                    bind.Setup(this, native);
                    mFindBindIndex = true;
                }
                else
                {
                    bind = (BindNode)mBindPool[index].Instance;
                }
                return bind;
            }

            protected override void OnMalloc(int size)
            {
                base.OnMalloc(size);
                mInvokeIDs.Dispose();
                mInvokeIDs = new NativeArray<InvokeID>(size, Allocator.Persistent);
                var ids = mSensorIDs;
                mSensorIDs = new NativeArray<int>(size, Allocator.Persistent);
                if (Length > 0)
                {
                    for (int i = 0; i < Length; i++)
                    {
                        mSensorIDs[i] = ids[i];
                    }
                }
                ids.Dispose();

#if UNITY_EDITOR
                Debug.LogFormat("Malloc {0}KB native memery for TransformSensor", CalculateNativeSize(ENativeSize.KB));
#endif
            }

            protected override void ResetNativeData(int index, INative<SensorData> inst)
            {
                base.ResetNativeData(index, inst);
                mSensorIDs[index] = inst.InstanceID;
            }

            public override double CalculateNativeSize(ENativeSize unit)
            {
                var cap = base.CalculateNativeSize(unit);
                if (mInvokeIDs.IsCreated)
                {
                    long size = (Marshal.SizeOf<InvokeID>() * mInvokeIDs.Length);
                    cap += size / (double)(1L << (int)unit);
                }
                if (mSensorIDs.IsCreated)
                {
                    long size = (Marshal.SizeOf<int>() * mSensorIDs.Length);
                    cap += size / (double)(1L << (int)unit);
                }
                if (mBindPool != null)
                {
                    cap += mBindPool.CalculateNativeSize(unit);
                }
                return cap;
            }

            public override void Dispose()
            {
                base.Dispose();
                if(mSensorIDs.IsCreated)
                {
                    mSensorIDs.Dispose();
                    mSensorIDs = default;
                }
                if (mInvokeIDs.IsCreated)
                {
                    mInvokeIDs.Dispose();
                    mInvokeIDs = default;
                }
                if (mBindPool != null)
                {
                    mBindPool.Dispose();
                    mBindPool = null;
                }
            }

            protected override void CompleteNativeJob()
            {
#if UNITY_EDITOR || COMMAND_PROFILER
                Profiler.BeginSample("Invoke Changed Transform");
#endif
                base.CompleteNativeJob();
                var size = Length;
                for (int i = 0; i < size; i++)
                {
                    var invoke = mInvokeIDs[i];
                    if (invoke.dirtyFlags == 0)
                        break;
                    var flags = (ETransformSensorFlag)invoke.dirtyFlags;
                    var sensor = (SensorNode)this[invoke.index].Instance;
                    var node = sensor.sensors.First;
                    while (node != null)
                    {
                        var v = node.Value;
                        v.OnTransformChanged(sensor.transform, flags);
                        node = node.Next;
                    }
                }
                mInvokeIDs[0] = new InvokeID(0, 0);
#if UNITY_EDITOR || COMMAND_PROFILER
                Profiler.EndSample();
#endif
            }

            protected override void RemoveAtSwapBack(int index)
            {
                base.RemoveAtSwapBack(index);
                if(index < Length)
                {
                    mSensorIDs[index] = mSensorIDs[Length];
                }
                mSensorIDs[Length] = 0;
                mFindBindIndex = true;
            }

            internal void ScheduleJob()
            {
#if UNITY_EDITOR || COMMAND_PROFILER
                Profiler.BeginSample("Schedule Jobs");
#endif

                /**
                 * Jobs strucutre:
                 * 
                 *   FindSensorIndexJob     TrackTransformJob
                 *         |_______________________|
                 *                    |
                 *             BindTransformJob
                 *                    |
                 *             UpdateInvokeIDJob
                 */
                JobHandle jobHandle = default;
                var findIndex = mFindBindIndex;
                if (findIndex)
                {
                    mFindBindIndex = false;
                    FindBindIndexJob findJob;
                    findJob.binds = mBindPool.NativeDatas;
                    findJob.sensors = mSensorIDs;
                    findJob.sensorSize = Length;
                    jobHandle = findJob.Schedule(mBindPool.Length, 128);
                }

                TrackTransformJob trackJob;
                trackJob.datas = NativeDatas;
                var exec = trackJob.Schedule(GetTransforms());

                BindTransformJob bindJob;
                bindJob.binds = mBindPool.NativeDatas;
                bindJob.sensors = NativeDatas;
                if (findIndex)
                    exec = bindJob.Schedule(mBindPool.GetTransforms(), JobHandle.CombineDependencies(jobHandle, exec));
                else
                    exec = bindJob.Schedule(mBindPool.GetTransforms(), exec);

                //mBindPool.SetNativeJob(exec);

                UpdateInvokeIDJob invokjob;
                invokjob.size = Length;
                invokjob.ids = mInvokeIDs;
                invokjob.datas = NativeDatas;
                exec = invokjob.Schedule(exec);

                SetNativeJob(exec);
#if UNITY_EDITOR || COMMAND_PROFILER
                Profiler.EndSample();
#endif
            }

#if UNITY_EDITOR

            static float _time;
            //HashSet<int> mSelections = new HashSet<int>();
            public void DrawBindGizmos()
            {
                if (mBindPool == null)
                    return;
                //mSelections.Clear();
                //var selected = UnityEditor.Selection.gameObjects;
                //for (int i = 0; i < selected.Length; i++)
                //{
                //    mSelections.Add(selected[i].transform.GetInstanceID());
                //}
                if (Application.isPlaying)
                    _time = Mathf.Repeat(_time + Time.unscaledDeltaTime, 500f);
                else
                    _time = Mathf.Repeat((float)UnityEditor.EditorApplication.timeSinceStartup, 500f);
                Gizmos.matrix = Matrix4x4.identity;
                for (int i = 0; i < mBindPool.Length; i++)
                {
                    var bind = mBindPool[i].Instance as BindNode;
                    if (bind.transform == null || bind.relativeTransform == null)
                        continue;
                    var p0 = bind.relativeTransform.position;
                    var p1 = bind.transform.position;
                    var dis = Vector3.SqrMagnitude(p1 - p0);
                    if (dis < 0.01f)
                        continue;
                    Gizmos.color = MathExt.DecodeRGB(MathExt.Hash(bind.transform.GetInstanceID(), 0x2f0a89));
                    Gizmos.DrawLine(p0, p1);
                    var t = Mathf.Repeat(_time * 0.5f + i * 0.1f, 1f);
                    var p = Vector3.Lerp(p1, p0, t);
                    var size = UnityEditor.HandleUtility.GetHandleSize(p) * 0.05f;
                    Gizmos.DrawCube(p, size * Vector3.one);

                }
            }
#endif
        }

        [SerializeField]
        int m_InitNativeSize = 1024;

        NativePool mSensorPool;

        //[SerializeField, EnableEdit(EEnableEditMode.Disabled)]
        [System.NonSerialized]
        bool m_IsActive;

        public bool m_DrawGizmos = true;

        int mLayer;
        int ICullEvent.Layer { get { return mLayer; } }


        bool IsCurrentSensor()
        {
            return sActiveSensor == this && mSensorPool != null;
        }

        private void OnEnable()
        {
            mLayer = gameObject.layer;
            if (sActiveSensor == null)
            {
                sActiveSensor = this;
            }
            if (sActiveSensor == this)
            {
#if UNITY_EDITOR
                if (Application.isPlaying)
#endif
                    DontDestroyOnLoad(gameObject);
                InitNativeMemery();
            }
            CullingManager.Instance.AddCullEvent(this);
        }

        private void OnDisable()
        {
            CullingManager.Instance.RemoveCullEvent(this);
            if (mSensorPool != null)
            {
                mSensorPool.FlushNativeJobs();
                mSensorPool.Dispose();
                mSensorPool = null;
            }
        }

        private void InitNativeMemery()
        {
            if (mSensorPool == null)
                mSensorPool = new NativePool(Mathf.Max(128, m_InitNativeSize));
        }

        void IBeforeCullingUpdate.OnBeforeCullingUpdate(Camera camera)
        {

//#if UNITY_EDITOR
//            gameObject.hideFlags &= ~HideFlags.HideInHierarchy;
//#endif

            m_IsActive = IsCurrentSensor();
            if(m_IsActive)
            {
                mSensorPool.FlushNativeJobs();
            }
            else if(this != null)
            {

#if UNITY_EDITOR
                if (!Application.isPlaying)
                    DestroyImmediate(gameObject);
                else
#endif
                    Destroy(gameObject);
            }
        }

        void IAfterCullingUpdate.OnAfterCullingUpdate(Camera camera)
        {
            if (m_IsActive)
            {
                mSensorPool.FlushNativeJobs();
                mSensorPool.ScheduleJob();
            }
        }

//        private void LateUpdate()
//        {
//#if UNITY_EDITOR
//            gameObject.hideFlags &= ~HideFlags.HideInHierarchy;
//#endif
//            m_IsActive = IsCurrentSensor();
//            if (m_IsActive)
//            {
//                mSensorPool.FlushNativeJobs();
//                mSensorPool.ScheduleJob();
//            }
//        }

        #endregion
   
        #region API
        static TransformSensor sActiveSensor;

        static TransformSensor EnsureTransformSensor(Object context)
        {
            if (!ParallelDispatcher.IsMainThread)
            {
                Debug.LogError("\"CreateTransformSensor\" can only be invoked on main thread.", context);
                return null;
            }
            if (sActiveSensor == null)
            {
#if UNITY_EDITOR
                sActiveSensor = GameObject.FindObjectOfType<TransformSensor>();
                if (sActiveSensor == null)
                {
                    var tname = string.Format("[TransformSensor_{0:x}]", GlobalUtil.GenUniqueID()); 
                    var go = new GameObject(tname, typeof(TransformSensor));
                    go.hideFlags |= HideFlags.DontSave;
                    sActiveSensor = go.GetComponent<TransformSensor>();
                }
#else
                var go = new GameObject("[TransformSensor]", typeof(TransformSensor));
                go.hideFlags |= HideFlags.DontSave;
                sActiveSensor = go.GetComponent<TransformSensor>();
#endif
            }
            sActiveSensor.InitNativeMemery();
            return sActiveSensor;
        }

        public static Sensor CreateTransformSensor(Transform trans, ITransformSensor sensor)
        {
            var sen = EnsureTransformSensor(trans);
            if (sen != null && trans != null && sensor != null)
                return sen.mSensorPool.CreateSensor(trans, sensor);
            else
                return null;
        }

        public static IRelativeTransform GetRelativeTransform(Transform trans)
        {
            var sen = EnsureTransformSensor(trans);
            if (sen != null && trans != null)
                return sen.mSensorPool.GetRelativeTransform(trans);
            else
                return default;
        }
        #endregion

#if UNITY_EDITOR

        [UnityEditor.MenuItem("Game Toolkit/Select TransformSensor")]
        static void SelectTransformSensor()
        {
            if (sActiveSensor != null)
            {
                UnityEditor.Selection.activeGameObject = sActiveSensor.gameObject;
            }
        }

        private void OnDrawGizmosSelected()
        {
            if (mSensorPool == null || !m_DrawGizmos || !m_IsActive)
                return;
            mSensorPool.DrawBindGizmos();
        }

        //[UnityEditor.MenuItem("Devil Framework/Select All TransformSensors")]
        //static void SelectAllTransformSensors()
        //{
        //    List<GameObject> targets = new List<GameObject>();
        //    for (int i = 0; i < SceneManager.sceneCount; i++)
        //    {
        //        var s = SceneManager.GetSceneAt(i);
        //        var gos = s.GetRootGameObjects();
        //        foreach (var go in gos)
        //        {
        //            if (go.GetComponent<TransformSensor>() != null)
        //            {
        //                targets.Add(go);
        //            }
        //        }
        //    }
        //    if (targets.Count > 0)
        //        UnityEditor.Selection.objects = targets.ToArray();
        //}
#endif

    }
}